/* 
	@Copyright (c) 2007 by Denis Riabtchik. All rights reserved. See license.txt and http://jgrouse.com for details@
	$Id: ContainerElement.java 574 2009-02-28 22:39:22Z denis.riabtchik $
*/
package jgrouse.jgdoc.elements;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jgrouse.jgdoc.JGDocException;
import jgrouse.jgdoc.Vocabulary;
import jgrouse.jgdoc.api.IComment;
import jgrouse.jgdoc.api.ICommentTag;
import jgrouse.jgdoc.api.IContainerElement;
import jgrouse.jgdoc.api.IDocElement;
import jgrouse.jgdoc.api.ILegacy;
import jgrouse.jgdoc.api.ILink;

 

public class ContainerElement extends BaseRefContainer implements IContainerElement, Serializable
{
	private static final long serialVersionUID = -3357530838503649706L;
	private List<ILegacy> m_legacies;
	
	private List<IContainerElement> m_subclasses;
	private List<IContainerElement> m_implementors;
	private List<IContainerElement> m_ancestors;
	 
	
	public ContainerElement()
	{
		super();
	}
	
	public ContainerElement(IComment pComment)
	{
		super(pComment);
	}
	
	
	
	@Override
	protected void init()
	{
		super.init();
		m_subclasses = new LinkedList<IContainerElement>();
		m_implementors = new LinkedList<IContainerElement>();
	}


	public void addChild(IDocElement element)
	{
		m_children.put(element.getLocalName(), new RefElement(element));
	}
	

	@Override
	protected String getText()
	{
		StringBuffer buf = new StringBuffer();
		if (m_children.size() > 0)
		{
			buf.append("<children>");
			for (RefElement de : m_children.values())
			{
				buf.append(de.toString());
			}			
			buf.append("</children>\n");
		}
		if (m_legacies != null)
		{
			buf.append("<legacies>\n");
			for (ILegacy l : m_legacies)
			{
				buf.append(l.toString());
			}
			buf.append("</legacies>\n");
		}
		return buf.toString();
	}

	private List<IContainerElement> walkHiearchy()
	{
		Set<String> visited = new HashSet<String>();
		List<IContainerElement> stack = new ArrayList<IContainerElement>();
		List<IContainerElement> current = new ArrayList<IContainerElement>();
		current.add(this);
		visited.add(this.getFullName());
		while (current.size() > 0)
		{
			List<IContainerElement> newSet = new ArrayList<IContainerElement>();
			for (IContainerElement de : current)
			{
				boolean wasVisited = visited.contains(de.getFullName());
				if (!wasVisited)
				{
					visited.add(de.getFullName());
				}
				List<IContainerElement> ancestors = de.getAncestors();
				if (ancestors != null)
				{
					for (IContainerElement a : ancestors)
					{
						if (newSet.indexOf(a) < 0 && !visited.contains(a.getFullName()))
						{
							newSet.add(a);
						}
					}
				}
				stack.remove(de);
				stack.add(de);
			}
			current = newSet;
		}
		if (stack.size() > 0)
		{
			stack.remove(0);
		}
		return stack;
	}

	private IDocElement findBaseMember(String pName, List<IContainerElement> pStack)
	{
		for (IContainerElement ce : pStack)
		{
			IDocElement de = ce.getChild(pName);
			if (de != null && de.getComment().getTagsForVocabulary(Vocabulary.INHERITDESC) == null)
			{
				return de;
			}
		}
		return null;
	}


	@Override
	public void postProcess() throws JGDocException
	{
		super.postProcess();
		List<IContainerElement> stack = walkHiearchy();
		m_legacies = new ArrayList<ILegacy>();
		Set<String> visited = new HashSet<String>();
		for (RefElement de : m_children.values())
		{
			visited.add(de.getRef().getLocalName());
		}
		for (IContainerElement ic : stack)
		{
			List<IDocElement> members = new ArrayList<IDocElement>();
			for (IDocElement de : ic.all())
			{
				if (!visited.contains(de.getLocalName()))
				{
					members.add(de);
					visited.add(de.getLocalName());
				}
			}
			if (members.size() != 0)
			{
				Legacy l = new Legacy(ic, members);
				m_legacies.add(l);
			}
		}
		for (RefElement childRef : m_children.values())
		{
			IDocElement child = childRef.getRef();
			IComment c = child.getComment();
			if (c != null)
			{
				List<ICommentTag> ctl = child.getComment().getTagsForVocabulary(Vocabulary.INHERITDESC);
				if (ctl != null)
				{
					ICommentTag ct = ctl.get(0);
					
					IDocElement doc = findBaseMember(child.getLocalName(), stack);
					if (doc == null)
					{
						child.getComment().dropTagsForVocabulary(Vocabulary.INHERITDESC);
						throw new JGDocException("Cannot find base member for " + child.getFullName() + ". This could also be a side effect of \"suppressPrivate\" setting.", child.getComment().getFile(), child.getComment().getLine());
					}
					ct.setName(doc.getFullName());
				}				
			}
		}
		
		if (m_comment == null)
		{
			setupAsNamespace();
		}
		
	}
	
	private void setupAsNamespace() throws JGDocException
	{
		m_comment = new Comment("", 0);
		CommentTag name = new CommentTag(Vocabulary.NAMESPACE, new Content(), null, getFullName(), null, m_comment);
		m_comment.addCommentTag(name);
		addAttribute("elementType", Vocabulary.NAMESPACE.getTokenType().toString().toLowerCase());
	}
	
	private void populateAncestors(List<ICommentTag> ext, Map<String, IDocElement> pNames, boolean isImplementor)
	{
		if (ext != null)
		{
			for (ICommentTag ct : ext)
			{
				ILink l = ct.getNameLink();
				if (l != null && l.getResolvedPath() != null)
				{
					IDocElement de = pNames.get(l.getResolvedPath());
					if (m_ancestors.indexOf(de) < 0 && de instanceof IContainerElement)
					{
						m_ancestors.add((IContainerElement) de);
						((IContainerElement) de).addSubclass(this);
						if (isImplementor)
						{
							((IContainerElement) de).addImplementor(this);
						}
					}
				}
			}
		}
	}

	@Override
	public void setupReferences(Map<String, IDocElement> pNames)
	{
		super.setupReferences(pNames);
		if (m_comment != null)
		{
			List<ICommentTag> ext = m_comment.getTagsForVocabulary(Vocabulary.EXTENDS);
			List<ICommentTag> inh = m_comment.getTagsForVocabulary(Vocabulary.IMPLEMENTS);
			m_ancestors = new ArrayList<IContainerElement>();
			populateAncestors(ext, pNames, false);
			populateAncestors(inh, pNames, true);
		}
	}

	
	private class Legacy implements Cloneable, ILegacy, Serializable
	{
		private static final long serialVersionUID = -8127695992425219221L;
		public IContainerElement m_ancestor;
		public List<IDocElement> m_members;
		
		public Legacy(IContainerElement pAncestor, List<IDocElement> pMembers)
		{
			m_ancestor = pAncestor;
			m_members = pMembers;
		}
		
		public ILegacy clone()
		{
			ILegacy l = new Legacy(m_ancestor, new ArrayList<IDocElement>(m_members));
			return l;
		}
		
		public IContainerElement getAncestor()
		{
			return m_ancestor;
		}
		
		public List<IDocElement> getMembers()
		{
			return m_members;
		}
		
		public List<IDocElement> get(String tagName)
		{
			List<IDocElement> result = new LinkedList<IDocElement>();
			for (IDocElement de : m_members)
			{
				if (de.getCommentType().equals(tagName))
				{
					result.add(de);
				}
			}
			return result;
		}
		
		public String toString()
		{
			StringBuffer buf = new StringBuffer("<legacy parent=\"").append(m_ancestor.getFullName()).append("\">\n");
			for (IDocElement member : m_members)
			{
				String s = member.getLocalName();
				buf.append("<legacyItem ref=\"").append(s);
				buf.append("\" type=\"").append(member.getComment().getCommentType().toString().toLowerCase());
				buf.append("\" idRef=\"").append(member.getFullName()).append("\"/>\n");
			}
			buf.append("</legacy>\n");
			return buf.toString();
		}
	}

	public List<IContainerElement> getAncestors()
	{
		return m_ancestors;
	}
	

	public List<ILegacy> getLegacies()
	{
		return m_legacies;
	}

	
	public boolean isLogicalContainer()
	{
		return true;
	}
	
	public void addSubclass(IContainerElement de)
	{
		m_subclasses.add(de);
	}
	
	public void addImplementor(IContainerElement de)
	{
		m_implementors.add(de);
	}
	
	public List<IContainerElement> getSubClasses()
	{
		return m_subclasses;
	}
	
	public List<IContainerElement> getImplementors()
	{
		return m_implementors;
	}

	public boolean isPureNamespace()
	{
		if (!"namespace".equals(getCommentType()))
		{
			return false;
		}
		for (RefElement rc : m_children.values())
		{
			IDocElement de = rc.getRef();
			if (!"namespace".equals(de.getCommentType()))
			{
				return false;
			}
		}
		return true;
	}
	
}
